﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Text.RegularExpressions;
using WMM.Tapioca.Linkbase;
using WMM.Tapioca.Instance;
using WMM.Tapioca.Taxonomy;
using WMM.Tapioca.Utilities;
using Saxon.Api;

namespace WMM.Tapioca.Formula.Filters
{
    /// <summary>
    /// The concept name filter matches facts based upon the names of their concepts. 
    /// </summary>
    public class ConceptNameFilter : Filter
    {
        private List<string> conceptNameValues;       

        /// <returns> List of concepts. </returns>
        public List<string> ConceptNameValues
        {
            get { return conceptNameValues; }
            set { conceptNameValues = value; }
        }

        private string expression;

        /// <summary>
        /// Constructor.
        /// </summary>
        public ConceptNameFilter(Resource resource, VariableSet variableSet)
            : base(resource)
        {
            ResourceName = resource.ResourceXElement.Name.LocalName;

            conceptNameValues = new List<string>();

            IEnumerable<XElement> conceptNames = resource.ResourceXElement.Descendants().Where(d => d.Name.LocalName.Equals("qname"));

            foreach (XElement conceptName in conceptNames)
            {
                string qname = conceptName.Value;
                qname = qname.TrimEnd().TrimStart();
                qname = Regex.Replace(qname, @"\s+", " ");
                conceptNameValues.Add(qname);
            }           

            IEnumerable<XElement> expressions = resource.ResourceXElement.Descendants().Where(d => d.Name.LocalName.Equals("qnameExpression"));

            foreach (XElement qnameExpression in expressions)
            {
                string expression = qnameExpression.Value;
                expression = expression.TrimEnd().TrimStart();
                expression = Regex.Replace(expression, @"\s+", " ");
                this.expression = expression;
            }      
        }

        public override List<FactItem> EvaluateFilter(VariableSet variableSet, List<FactItem> itemsToFilter, DTS dts)
        {
            this.dts = dts;

            List<FactItem> itemListTemp = new List<FactItem>();            

            if (this.expression != null)
            {
                string thisVariableName = GetVariableName(this.Label, variableSet);

                Vertex thisVertex = GetVertexThisVariable(variableSet, thisVariableName);

                List<string> dependecyVariables = new List<string>();

                if (thisVertex != null)
                {
                    foreach (Edge edge in thisVertex.outEdges)
                    {
                        dependecyVariables.Add(edge.destination.name);
                    }
                }

                conceptNameValues.AddRange(EvaluateQnameExpression(dependecyVariables, variableSet, this.expression, this));
            }

            foreach (FactItem item in itemsToFilter)
            {
                foreach (string qname in ConceptNameValues)
                {
                    Concept concept = dts.GetConceptByName(qname.GetLocalName());

                    if (concept.Id.Equals(item.Concept.Id))
                    {
                        itemListTemp.Add(item);
                    }
                }
            }

            return itemListTemp;
        }


        private List<string> EvaluateQnameExpression(List<string> dependecyVariables, VariableSet variableSet, string expression, Filter filter)
        {
            List<string> conceptsName = new List<string>();

            // Create a Processor instance.
            Processor processor = new Processor();

            RegisterExtensionFunctions(processor);

            // Create the XPath expression.
            XPathCompiler compiler = processor.NewXPathCompiler();

            DeclareVariables2(dependecyVariables, variableSet, compiler);

            XPathSelector selector = compiler.Compile(expression).Load();

            SetContextItem(processor, selector, variableSet.dts.Instance.ThisXDocument.Root.GetXmlNode());

            variableSet.BindParameters(selector, variableSet, null);

            BindGeneralVariable(variableSet, compiler, selector);

            foreach (string variableName in dependecyVariables)
            {
                List<FactItem> factItems = variableSet.variableItems[variableName];

                foreach (FactItem item in factItems)
                {
                    BindVariableValue(processor, variableSet, selector, variableName, item);

                    conceptsName.Add(selector.Evaluate().ToString());
                }
            }

            return conceptsName;
        }
    }
}
